/**
 * Software License, Version 1.0
 * 
 * Copyright 2003 The Trustees of Indiana University.  All rights reserved.
 * 
 *
 *Redistribution and use in source and binary forms, with or without 
 *modification, are permitted provided that the following conditions are met:
 *
 *1) All redistributions of source code must retain the above copyright notice,
 * the list of authors in the original source code, this list of conditions and
 * the disclaimer listed in this license;
 *2) All redistributions in binary form must reproduce the above copyright 
 * notice, this list of conditions and the disclaimer listed in this license in
 * the documentation and/or other materials provided with the distribution;
 *3) Any documentation included with all redistributions must include the 
 * following acknowledgement:
 *
 *"This product includes software developed by the Community Grids Lab. For 
 * further information contact the Community Grids Lab at 
 * http://communitygrids.iu.edu/."
 *
 * Alternatively, this acknowledgement may appear in the software itself, and 
 * wherever such third-party acknowledgments normally appear.
 * 
 *4) The name Indiana University or Community Grids Lab or NaradaBrokering, 
 * shall not be used to endorse or promote products derived from this software 
 * without prior written permission from Indiana University.  For written 
 * permission, please contact the Advanced Research and Technology Institute 
 * ("ARTI") at 351 West 10th Street, Indianapolis, Indiana 46202.
 *5) Products derived from this software may not be called NaradaBrokering, 
 * nor may Indiana University or Community Grids Lab or NaradaBrokering appear
 * in their name, without prior written permission of ARTI.
 * 
 *
 * Indiana University provides no reassurances that the source code provided 
 * does not infringe the patent or any other intellectual property rights of 
 * any other entity.  Indiana University disclaims any liability to any 
 * recipient for claims brought by any other entity based on infringement of 
 * intellectual property rights or otherwise.  
 *
 *LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH NO 
 *WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. INDIANA UNIVERSITY GIVES
 *NO WARRANTIES AND MAKES NO REPRESENTATION THAT SOFTWARE IS FREE OF 
 *INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR OTHER PROPRIETARY RIGHTS. 
 *INDIANA UNIVERSITY MAKES NO WARRANTIES THAT SOFTWARE IS FREE FROM "BUGS", 
 *"VIRUSES", "TROJAN HORSES", "TRAP DOORS", "WORMS", OR OTHER HARMFUL CODE.  
 *LICENSEE ASSUMES THE ENTIRE RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR 
 *ASSOCIATED MATERIALS, AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION 
 *GENERATED USING SOFTWARE.
 */
package cgl.narada.wsinfra.deployment;

import javax.xml.soap.SOAPMessage;

import cgl.narada.wsinfra.WsMessageFlow;
import cgl.narada.wsinfra.exception.DeploymentException;
import cgl.narada.wsinfra.exception.IncorrectExchangeException;
import cgl.narada.wsinfra.exception.MessageFlowException;
import cgl.narada.wsinfra.exception.ProcessingException;
import cgl.narada.wsinfra.exception.UnknownExchangeException;

/** This abstract class implements the functionality of a filter within our
    deployment environment. This class implements the WsMessageFlow interface.
    Note that the only method that needs to be implemented by filters extending
    this base class is the processMessage class. For examples of filters 
    extending this baseclass, please see the filters in the cgl.narada.wsinfra.
    apps package; here, examples pertaining to WSE and WSRM filters have been
    included.

    @author Shrideep Pallickara
    $Date: 2005/07/29 22:41:29 $
    $Revision: 1.8 $
*/

public abstract class Filter implements WsMessageFlow {
  public static final int INPUT = 1;
  public static final int OUTPUT = 2;
  public static final int INPUT_OUTPUT = 3;
  
  /** By default every filter is configured to be an input output filter.*/
  private int direction = INPUT_OUTPUT;
  private String identifier;
  private FilterPipeline filterPipeline;
  private boolean isAddedToFilterPipeline = false;
  private String moduleName = "Filter: ";

  /** Retrieve the filter pipeline that this filter is a part of. */
  public final void setFilterPipeline(FilterPipeline filterPipeline) {
    this.filterPipeline = filterPipeline;
  }

  /** Retrieve the filter pipeline that this filter is a part of. */
  public final FilterPipeline getFilterPipeline() {
    return filterPipeline;
  }
  

  /** Retrieves the position of this filter in the pipeline. */
  public final int getPositionInFilterPipeline() throws DeploymentException {
    if (filterPipeline == null) {
      String reason = "This particular filter [" + identifier + "] is NOT" +
	" currently part of a FilterPipeline. ";
      throwDeploymentException(reason);
    }
    
    int position = filterPipeline.getPositionInFilterPipeline(this);
    return position;
  }
  

  /** Set the identifier of the filter.*/
  public final void setIdentifier(String identifier) {
    this.identifier = identifier;
  }


  /** Retrieve the identifier of the filter.*/
  public final String getIdentifier() {
    return identifier;
  }


  /** Sets the direction of messages that this filter should operate upon. */
  public final void setDirection(int directionOfFlow) 
    throws DeploymentException {
    
    if (direction != INPUT || direction != OUTPUT 
	|| direction != INPUT_OUTPUT) {
      String reason = "The specified direction [" + directionOfFlow + 
	"] is not a valid one.";
      throwDeploymentException(reason);
    }
    
    direction = directionOfFlow;
  }

  /** Is this an input filter. */
  public final boolean isInputFilter() {
    if (direction == INPUT || direction == INPUT_OUTPUT) {
      return true;
    }
    return false;
  }


  /** Is this an output filter */
  public final boolean isOutputFilter() {
    if (direction == OUTPUT || direction == INPUT_OUTPUT) {
      return true;
    }
    return false;
  }

  

  /** Retrieves the direction of messages that this filter operates upon. */
  public final int getDirection() {
    return direction;
  }


  /** Routes a message enroute to the application. The message is basically 
      routed to a neighboring filter which is nearer to the application. */
  public final void enrouteToApplication(SOAPMessage soapMessage)
    throws MessageFlowException{

    try {
      checkForFilterPipelineProblems();
      checkForSoapMessageProblems(soapMessage);
      
      int position = filterPipeline.getPositionInFilterPipeline(this);
      position--;
      filterPipeline.injectMessageTowardsApplication(soapMessage, position);
    } catch (DeploymentException de) {
      throwMessageFlowException(de.toString());
    }
  }
    

  
  /** Routes a message enroute to the network. The message is basically 
      routed to a neighboring filter which is closer to the network side of
      the filter-pipeline. */
  public final void enrouteToNetwork(SOAPMessage soapMessage) 
    throws MessageFlowException  {
    
    try {
      checkForFilterPipelineProblems();
      checkForSoapMessageProblems(soapMessage);
      
      int position = filterPipeline.getPositionInFilterPipeline(this);
      position++;
      filterPipeline.injectMessageTowardsNetwork(soapMessage, position);

    } catch (DeploymentException de) {
      throwMessageFlowException(de.toString());
    } catch (ProcessingException pe) {
      throwMessageFlowException(pe.toString());
    }


  }


  
  /** This method returns a boolean which indicates whether further 
      processing should continue or if it should stop. A return value of
      <i>true</i> indicates that processing should continue; while <i>false</i>
      indicates that processing should stop. Note that it is the 
      filter-pipeline which is responsible for stopping this processing. */
  public abstract boolean 
  processMessage(SOAPContext soapContext, int direction) 
    throws UnknownExchangeException, IncorrectExchangeException, 
    MessageFlowException, ProcessingException;


  /** Dipose this filter. This method will also remove this filter from a
      pipeline, if indeed it is part of one. It returns a boolean variable
      indicating whether the operation was successful or not. */
  public final void dispose() throws DeploymentException {
    if (filterPipeline != null) {
      if (filterPipeline.isPartOfPipeline(this)) {
	filterPipeline.removeFilter(this);
	return;
      }
    }        
  }

  
  /**
     Compares two objects for equality. Returns a boolean that indicates
     whether this object is equivalent to the specified object. This method
     is used when an object is stored in a hashtable.
     @param obj the Object to compare with
     @return true if these Objects are equal; false otherwise.
     @see java.util.Hashtable
  */
  public final boolean equals(Filter obj) {
    if (obj == null || identifier == null) {
      return false;
    }
    
    if (obj.getIdentifier().equals(identifier)) {
      return true;
    }

    return false;
  }

  /**Generates a hash code for the receiver. This method is supported
     primarily for hash tables, such as those provided in java.util.
     If two objects are equal (equals(Object) returns true) they must have the
     same hash code
     @return an integer hash code for the receiver  */
  public final int hashCode() {
    if (identifier == null) {
      return 0;
    }
    return identifier.hashCode();
  }
  

  private void checkForFilterPipelineProblems() 
    throws MessageFlowException {
    if (filterPipeline == null) {
      String reason = "This particular filter [" + identifier + "] is NOT" +
	" currently part of a FilterPipeline. ";
      throwMessageFlowException(reason);
    }
  }

  
  private void checkForSoapMessageProblems(SOAPMessage soapMessage) 
    throws MessageFlowException {
    if (filterPipeline == null) {
      String reason = "The specified SOAPMessage is NULL.";
      throwMessageFlowException(reason);
    }
  }

  private void throwDeploymentException(String reason) 
    throws DeploymentException {
    
    String errorReport = moduleName + reason;
    DeploymentException dEx = new DeploymentException(errorReport);
    throw dEx;
  }


  private void throwMessageFlowException(String reason) 
    throws MessageFlowException {

    String errorReport = moduleName + reason;
    MessageFlowException mfEx = new MessageFlowException(errorReport);
    throw mfEx;

  }

}
